package com.have.blog.xo.service.admin.impl;


import com.have.blog.base.constant.BlogConstant;
import com.have.blog.base.enums.AdminMenuEnum;
import com.have.blog.commons.config.jwt.Audience;
import com.have.blog.commons.feign.PictureFeignClient;
import com.have.blog.model.mapper.TAdminMapper;
import com.have.blog.model.mapper.TCategoryMenuMapper;
import com.have.blog.model.mapper.TRoleMapper;
import com.have.blog.model.models.*;
import com.have.blog.utils.AccountCheckUtils;
import com.have.blog.utils.IpUtils;
import com.have.blog.utils.MyStringUtils;
import com.have.blog.utils.ResultUtils;
import com.have.blog.xo.bo.admin.AdminUserDetail;
import com.have.blog.xo.bo.admin.OnlineAdmin;
import com.have.blog.xo.constant.MessageConstant;
import com.have.blog.xo.constant.SysConstant;
import com.have.blog.xo.service.cache.AdminCacheService;
import com.have.blog.xo.service.admin.AdminLoginService;
import com.have.blog.xo.uitls.WebUtil;
import com.have.haveblog.security.utils.JwtTokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
@SuppressWarnings("all")
public class AdminLoginServiceImpl implements AdminLoginService {
    @Autowired
    private AdminCacheService adminCacheService;

    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private Audience audience;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private TAdminMapper adminMapper;
    @Autowired
    private TRoleMapper roleMapper;
    @Autowired
    private TCategoryMenuMapper categoryMenuMapper;
    @Autowired
    private PictureFeignClient pictureFeignClient;
    @Autowired
    private WebUtil webUtil;


    @Value("${redis.admin.isRememberMeExpiredSeconds}")
    private long isRemeberMeExpiredSeconds;
    @Value("${jwt.tokenHead}")
    private String tokenHead;

    /**
     * 登录服务
     * @param request
     * @param username
     * @param password
     * @param isRememberMe
     * @return
     */
    @Override
    public String login(HttpServletRequest request, String username, String password, Boolean isRememberMe) {
        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) { // 验证账号密码
            return ResultUtils.result(SysConstant.ERROR, "账号或密码不能为空");
        }
        // 1. 根据Ip地址到redis中查询用户登录错误次数，如果大于阈值则拒绝访问。
        String ipAddr = IpUtils.getIpAddr(request);
        String loginErrorCount = adminCacheService.getLoginErrorCount(ipAddr);
        if (!StringUtils.isEmpty(loginErrorCount)) {
            if (Integer.valueOf(loginErrorCount) >= BlogConstant.NUM_5000) {
                return ResultUtils.result(SysConstant.ERROR, "密码错误次数过多，已被锁定30分钟");
            }
        }

        // 3. 查询用户
        UserDetails userDetails = loadAdminByUsername(username);
        if (userDetails == null) {
            return ResultUtils.result(SysConstant.ERROR, String.format(MessageConstant.LOGIN_ERROR, setLoginCommit(request)));
        }
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);

        if (!passwordEncoder.matches(password, userDetails.getPassword())) { // 密码错误, 返回错误信息，更新缓存
            return ResultUtils.result(SysConstant.ERROR, String.format(MessageConstant.LOGIN_ERROR, setLoginCommit(request)));
        }
        TAdmin admin = ((AdminUserDetail) userDetails).getAdmin();
        String roleName = ((AdminUserDetail) userDetails).getRoleName();
        // 4. 根据是否记住我设置token过期时间, token相关
        long expiredTime = isRememberMe ? isRemeberMeExpiredSeconds : audience.getExpiredSeconds();
        String jwtToken = jwtTokenUtil.generateJwtToken(userDetails.getUsername(),
                admin.getUid(),
                audience.getClientId(),
                audience.getIssuer(),
                expiredTime * 1000,
                audience.getBase64Secret());
        Map<String, Object> resultMap = new HashMap<>(BlogConstant.NUM_ONE);
        resultMap.put(SysConstant.TOKEN, jwtToken);
        // 5. 更新数据库中相关用户信息
        updateAdmin(admin, ipAddr);
        // 6. 添加用户到缓存
        adminCacheService.addOnlineAdmin(admin, jwtToken, MyStringUtils.getUUID(), roleName, expiredTime);
        return ResultUtils.result(SysConstant.SUCCESS, resultMap);
    }

    /**
     * 获取用户信息
     * @return
     */
    @Override
    public String info() {
        ServletRequestAttributes attribute = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attribute.getRequest();
        String jwtToken = (String) request.getAttribute(SysConstant.TOKEN);
        if (StringUtils.isEmpty(jwtToken)) {
            return ResultUtils.result(SysConstant.ERROR, MessageConstant.NOT_LOGIN);
        }
        HashMap<String, Object> map = new HashMap<>(BlogConstant.NUM_THREE);


        String username = jwtTokenUtil.getUsername(jwtToken, audience.getBase64Secret());
        TAdmin admin = getAdminByUsername(username);
        TRole role = loadRoleByUid(admin.getRoleUid());
        ArrayList<TRole> tRoles = new ArrayList<>();
        tRoles.add(role);
        map.put(SysConstant.ROLE, tRoles);
        map.put(SysConstant.TOKEN, jwtToken);
        if (!StringUtils.isEmpty(admin.getAvatar())) {
            String pictureInfo = pictureFeignClient.getPicture(admin.getAvatar(), "");
            List<Map<String, Object>> pictureMap = webUtil.getPictureMap(pictureInfo);
            List<String> urlList= new ArrayList<>();
            for (Map<String, Object> item : pictureMap) {
                urlList.add((String) item.get(SysConstant.URL));
            }
            map.put(SysConstant.AVATAR, urlList.get(0));
        } else {
            map.put(SysConstant.AVATAR, "https://gitee.com/moxi159753/wx_picture/raw/master/picture/favicon.png");
        }
        return ResultUtils.result(SysConstant.SUCCESS, map);
    }

    /**
     * 退出登录
     * @return
     */
    @Override
    public String logout() {
        ServletRequestAttributes attribute = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attribute.getRequest();
        String token = (String) request.getAttribute(SysConstant.TOKEN);
        if (StringUtils.isEmpty(token)) {
            return ResultUtils.result(SysConstant.SUCCESS, MessageConstant.OPERATION_SUCCESS);
        }
        // 删除缓存中的用户信息
        OnlineAdmin onlineAdmin = adminCacheService.getOnlineAdmin(token);
        System.out.println(onlineAdmin);
        if (onlineAdmin != null) {
            String tokenUId = onlineAdmin.getTokenUId();
            adminCacheService.deleteToken(tokenUId);
            adminCacheService.deleteOnlineAdmin(token);
        }
        return ResultUtils.result(SysConstant.SUCCESS, MessageConstant.OPERATION_SUCCESS);
    }

    @Override
    public String getMenu() {
        ServletRequestAttributes attribute = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attribute.getRequest();
        String jwtToken = (String) request.getAttribute(SysConstant.TOKEN);
        if (StringUtils.isEmpty(jwtToken)) {
            return ResultUtils.errorWithMessage(MessageConstant.NOT_LOGIN);
        }
        String adminUid = jwtTokenUtil.getAdminUid(jwtToken, audience.getBase64Secret());
        List<TCategoryMenu> categoryMenuList = adminCacheService.getResourceList(adminUid);
        if (CollectionUtils.isEmpty(categoryMenuList)) {
            TAdmin admin = adminMapper.selectByPrimaryKey(adminUid);
            TRole role = roleMapper.selectByPrimaryKey(admin.getRoleUid());
            categoryMenuList = getResourceList(role.getCategoryMenuUids());
            adminCacheService.addResourceList(admin.getUid(), categoryMenuList, (long) 30, TimeUnit.MINUTES);
        }

        // 从三级级分类中查询出 二级分类
        List<TCategoryMenu> buttonList = new ArrayList<>();
        Set<String> secondMenuUidList = new HashSet<>();
        categoryMenuList.forEach(item -> {
            // 查询二级分类
            if (item.getMenuType() == AdminMenuEnum.MENU.getType() && item.getMenuLevel() == SysConstant.TWO) {
                secondMenuUidList.add(item.getUid());
            }
            // 从三级分类中，得到二级分类
            if (item.getMenuType() == AdminMenuEnum.BUTTON.getType() && !StringUtils.isEmpty(item.getParentUid())) {
                // 找出二级菜单
                secondMenuUidList.add(item.getParentUid());
                // 找出全部按钮
                buttonList.add(item);
            }
        });

        Collection<TCategoryMenu> childCategoryMenuList = new ArrayList<>();
        Collection<TCategoryMenu> parentCategoryMenuList = new ArrayList<>();
        List<String> parentCategoryMenuUids = new ArrayList<>();

        TCategoryMenuExample example = new TCategoryMenuExample();
        example.createCriteria().andUidIn(new ArrayList<>(secondMenuUidList));
        childCategoryMenuList = categoryMenuMapper.selectByExample(example);
        childCategoryMenuList.forEach(item -> {
            //选出所有的二级分类
            if (item.getMenuLevel() == SysConstant.TWO) {

                if (!StringUtils.isEmpty(item.getParentUid())) {
                    parentCategoryMenuUids.add(item.getParentUid());
                }
            }
        });

        if (parentCategoryMenuUids.size() > 0) {
            TCategoryMenuExample example1 = new TCategoryMenuExample();
            example1.createCriteria().andUidIn(parentCategoryMenuUids);
            parentCategoryMenuList = categoryMenuMapper.selectByExample(example1);
        }

        List<TCategoryMenu> list = new ArrayList<>(parentCategoryMenuList);

        //对parent进行排序
        Map<String, Object> map = new HashMap<>(BlogConstant.NUM_THREE);
        Collections.sort(list, new Comparator<TCategoryMenu>() {
            @Override
            public int compare(TCategoryMenu o1, TCategoryMenu o2) {
                return o1.getSort() - o2.getSort();
            }
        });
        map.put(SysConstant.PARENT_LIST, list);
        map.put(SysConstant.SON_LIST, childCategoryMenuList);
        map.put(SysConstant.BUTTON_LIST, buttonList);
        return ResultUtils.result(SysConstant.SUCCESS, map);
    }

    /**
     * 更新用户信息
     * @param admin
     * @param ip
     */
    private void updateAdmin(TAdmin admin, String ip) {
        Integer count = admin.getLoginCount();
        admin.setLoginCount(count + 1);
        admin.setLastLoginIp(ip);
        admin.setLastLoginTime(new Date());
        TAdminExample example = new TAdminExample();
        TAdminExample.Criteria criteria = example.createCriteria();
        criteria.andUidEqualTo(admin.getUid());
        adminMapper.updateByExampleSelective(admin, example);
    }

    /**
     * 设置登录限制,返回剩余次数
     */
    private Integer setLoginCommit(HttpServletRequest request) {
        String ipAddr = IpUtils.getIpAddr(request);
        String loginErrorCount = adminCacheService.getLoginErrorCount(ipAddr);
        Integer surplusCount = BlogConstant.NUM_5000;
        if (!StringUtils.isEmpty(loginErrorCount)) {
            Integer tempCount = Integer.valueOf(loginErrorCount) + 1;
            surplusCount -= tempCount;
            adminCacheService.setLoginErrorCount(ipAddr, String.valueOf(tempCount), 10, TimeUnit.MINUTES);
        } else {
            surplusCount -= 1;
            adminCacheService.setLoginErrorCount(ipAddr, BlogConstant.STR_ONE, 30, TimeUnit.MINUTES);
        }
        return surplusCount;
    }

    /**
     * 获取用户资源列表
     * @param adminUid
     * @param categoryMenyUids
     * @return
     */
    private List<TCategoryMenu> getResourceList(String categoryMenyUids) {
        // 1. 先查看缓存中是否存在资源列表

        // 2. 缓存中不存在查数据库
        List<String> uids = uidsStringToList(categoryMenyUids);
        TCategoryMenuExample example = new TCategoryMenuExample();
        TCategoryMenuExample.Criteria criteria = example.createCriteria();
        criteria.andUidIn(uids);
        List<TCategoryMenu> categoryMenus = categoryMenuMapper.selectByExample(example);
        return categoryMenus;
    }

    /**
     * 数据库中储存的categoryMenuUid形式是text，需要将其从字符串转换为list
     * @param categoryMenyUids
     * @return
     */
    private List<String> uidsStringToList(String categoryMenyUids) {
        categoryMenyUids = categoryMenyUids.substring(1, categoryMenyUids.length());
        categoryMenyUids = categoryMenyUids.substring(0, categoryMenyUids.length() - 1);
        String[] split = categoryMenyUids.split(",");
        for (int i = 0; i < split.length; i++) {
            split[i] = split[i].substring(1, split[i].length());
            split[i] = split[i].substring(0, split[i].length()-1);
        }
        List<String> resultList = Arrays.asList(split);
        return resultList;
    }

    /**
     * 根据登录账号获取用户
     * @param usernmae
     * @return
     */
    public TAdmin getAdminByUsername(String usernmae) {
        boolean isEmail = AccountCheckUtils.isEmail(usernmae);
        boolean isPhone = AccountCheckUtils.isPhoneNumber(usernmae);
        TAdminExample adminExample = new TAdminExample();
        if (isEmail) {
            adminExample.createCriteria().andEmailEqualTo(usernmae);
        } else if (isPhone) {
            adminExample.createCriteria().andMobileEqualTo(usernmae);
        } else {
            adminExample.createCriteria().andUserNameEqualTo(usernmae);
        }
        TAdmin admin;
        List<TAdmin> admins = adminMapper.selectByExample(adminExample);
        if (admins.size() > 0) {
            admin = admins.get(0);
        } else { // 账号错误，未找到用户
            return null;
        }
        return admin;
    }

    /**
     * 加载用户为UserDetials
     * @param username
     * @return
     */
    public UserDetails loadAdminByUsername(String username) {
        TAdmin admin = getAdminByUsername(username);
        if (admin != null) {
            TRole role = loadRoleByUid(admin.getRoleUid());
            if (role == null) {
                return new AdminUserDetail(admin, new ArrayList<>(), role.getRoleName());
            }
            /**
             * 先去缓存中查找资源，不存在就查数据库
             */
            List<TCategoryMenu> resourceList = adminCacheService.getResourceList(admin.getUid());
            if (CollectionUtils.isEmpty(resourceList)) {
                resourceList = getResourceList(role.getCategoryMenuUids());
                adminCacheService.addResourceList(admin.getUid(), resourceList, (long)30, TimeUnit.MINUTES);
            }
            return new AdminUserDetail(admin, resourceList, role.getRoleName());
        }
        return null;
    }

    /**
     * 根据RoleUid获取角色
     * @param roleUid
     * @return
     */
    private TRole loadRoleByUid(String roleUid) {
        TRoleExample roleExample = new TRoleExample();
        TRoleExample.Criteria criteria1 = roleExample.createCriteria();
        criteria1.andUidEqualTo(roleUid);
        List<TRole> roles = roleMapper.selectByExampleWithBLOBs(roleExample);
        if (roles.size() < 1) { // 用户没有权限
            return null;
        }
        return roles.get(0);
    }
}
